/*
* CL_newfx.java
* Copyright (C) 2004
* 
* $Id: CL_newfx.java,v 1.7 2005/01/17 21:50:42 cawe Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// Created on 31.01.2004 by RST.
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Lib = Suake2.UI.util.Lib;
using Math3D = Suake2.UI.util.Math3D;
namespace Suake2.UI.client
{
	
	/// <summary> CL_newfx</summary>
	public class CL_newfx
	{
		
		internal static void  Flashlight(int ent, float[] pos)
		{
			CL_fx.cdlight_t dl;
			
			dl = CL_fx.AllocDlight(ent);
			Math3D.VectorCopy(pos, dl.origin);
			dl.radius = 400;
			dl.minlight = 250;
			dl.die = Globals.cl.time + 100;
			dl.color[0] = 1;
			dl.color[1] = 1;
			dl.color[2] = 1;
		}
		
		/*
		* ====== CL_ColorFlash - flash of light ======
		*/
		internal static void  ColorFlash(float[] pos, int ent, int intensity, float r, float g, float b)
		{
			CL_fx.cdlight_t dl;
			
			if ((Globals.vidref_val == Defines.VIDREF_SOFT) && ((r < 0) || (g < 0) || (b < 0)))
			{
				intensity = - intensity;
				r = - r;
				g = - g;
				b = - b;
			}
			
			dl = CL_fx.AllocDlight(ent);
			Math3D.VectorCopy(pos, dl.origin);
			dl.radius = intensity;
			dl.minlight = 250;
			dl.die = Globals.cl.time + 100;
			dl.color[0] = r;
			dl.color[1] = g;
			dl.color[2] = b;
		}
		
		// stack variable
		//UPGRADE_NOTE: Final was removed from the declaration of 'move'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] move = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'vec'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] vec = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'right'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] right = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'up'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] up = new float[]{0, 0, 0};
		/*
		* ====== CL_DebugTrail ======
		*/
		internal static void  DebugTrail(float[] start, float[] end)
		{
			float len;
			//		int j;
			cparticle_t p;
			float dec;
			//		int i;
			//		float d, c, s;
			//		float[] dir;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			Math3D.MakeNormalVectors(vec, right, up);
			
			//		VectorScale(vec, RT2_SKIP, vec);
			
			//		dec = 1.0;
			//		dec = 0.75;
			dec = 3;
			Math3D.VectorScale(vec, dec, vec);
			Math3D.VectorCopy(start, move);
			
			while (len > 0)
			{
				len -= dec;
				
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				Math3D.VectorClear(p.accel);
				Math3D.VectorClear(p.vel);
				p.alpha = 1.0f;
				p.alphavel = - 0.1f;
				//			p.alphavel = 0;
				p.color = 0x74 + (Lib.rand() & 7);
				Math3D.VectorCopy(move, p.org);
				/*
				* for (j=0 ; j <3 ; j++) { p.org[j] = move[j] + crand()*2; p.vel[j] =
				* crand()*3; p.accel[j] = 0; }
				*/
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		// stack variable
		// move, vec
		internal static void  ForceWall(float[] start, float[] end, int color)
		{
			float len;
			int j;
			cparticle_t p;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			Math3D.VectorScale(vec, 4, vec);
			
			// FIXME: this is a really silly way to have a loop
			while (len > 0)
			{
				len -= 4;
				
				if (CL_fx.free_particles == null)
					return ;
				
				if ((float) Globals.rnd.NextDouble() > 0.3)
				{
					p = CL_fx.free_particles;
					CL_fx.free_particles = p.next;
					p.next = CL_fx.active_particles;
					CL_fx.active_particles = p;
					Math3D.VectorClear(p.accel);
					
					p.time = Globals.cl.time;
					
					p.alpha = 1.0f;
					p.alphavel = (- 1.0f) / (3.0f + (float) Globals.rnd.NextDouble() * 0.5f);
					p.color = color;
					for (j = 0; j < 3; j++)
					{
						p.org[j] = move[j] + Lib.crand() * 3;
						p.accel[j] = 0;
					}
					p.vel[0] = 0;
					p.vel[1] = 0;
					p.vel[2] = - 40 - (Lib.crand() * 10);
				}
				
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		// stack variable
		// move, vec
		/*
		* =============== CL_BubbleTrail2 (lets you control the # of bubbles by
		* setting the distance between the spawns)
		* 
		* ===============
		*/
		internal static void  BubbleTrail2(float[] start, float[] end, int dist)
		{
			float len;
			int i, j;
			cparticle_t p;
			float dec;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			dec = dist;
			Math3D.VectorScale(vec, dec, vec);
			
			for (i = 0; i < len; i = (int) (i + dec))
			{
				if (CL_fx.free_particles == null)
					return ;
				
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				Math3D.VectorClear(p.accel);
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = (- 1.0f) / (1 + (float) Globals.rnd.NextDouble() * 0.1f);
				p.color = 4 + (Lib.rand() & 7);
				for (j = 0; j < 3; j++)
				{
					p.org[j] = move[j] + Lib.crand() * 2;
					p.vel[j] = Lib.crand() * 10;
				}
				p.org[2] -= 4;
				//			p.vel[2] += 6;
				p.vel[2] += 20;
				
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		// stack variable
		// move, vec, right, up
		//UPGRADE_NOTE: Final was removed from the declaration of 'dir'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] dir = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'end'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] end = new float[]{0, 0, 0};
		
		internal static void  Heatbeam(float[] start, float[] forward)
		{
			float len;
			int j;
			cparticle_t p;
			int i;
			float c, s;
			float ltime;
			float step = 32.0f, rstep;
			float start_pt;
			float rot;
			float variance;
			
			Math3D.VectorMA(start, 4096, forward, end);
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			// FIXME - pmm - these might end up using old values?
			//		MakeNormalVectors (vec, right, up);
			Math3D.VectorCopy(Globals.cl.v_right, right);
			Math3D.VectorCopy(Globals.cl.v_up, up);
			if (Globals.vidref_val == Defines.VIDREF_GL)
			{
				// GL mode
				Math3D.VectorMA(move, - 0.5f, right, move);
				Math3D.VectorMA(move, - 0.5f, up, move);
			}
			// otherwise assume SOFT
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			ltime = (float) Globals.cl.time / 1000.0f;
			start_pt = ltime * 96.0f % step;
			Math3D.VectorMA(move, start_pt, vec, move);
			
			Math3D.VectorScale(vec, step, vec);
			
			//		Com_Printf ("%f\n", ltime);
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			rstep = (float) (System.Math.PI / 10.0);
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			float M_PI2 = (float) (System.Math.PI * 2.0);
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			for (i = (int) start_pt; i < len; i = (int) (i + step))
			{
				if (i > step * 5)
				// don't bother after the 5th ring
					break;
				
				for (rot = 0; rot < M_PI2; rot += rstep)
				{
					
					if (CL_fx.free_particles == null)
						return ;
					
					p = CL_fx.free_particles;
					CL_fx.free_particles = p.next;
					p.next = CL_fx.active_particles;
					CL_fx.active_particles = p;
					
					p.time = Globals.cl.time;
					Math3D.VectorClear(p.accel);
					//				rot+= fmod(ltime, 12.0)*M_PI;
					//				c = cos(rot)/2.0;
					//				s = sin(rot)/2.0;
					//				variance = 0.4 + ((float)rand()/(float)RAND_MAX) *0.2;
					variance = 0.5f;
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					c = (float) (System.Math.Cos(rot) * variance);
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					s = (float) (System.Math.Sin(rot) * variance);
					
					// trim it so it looks like it's starting at the origin
					if (i < 10)
					{
						Math3D.VectorScale(right, c * (i / 10.0f), dir);
						Math3D.VectorMA(dir, s * (i / 10.0f), up, dir);
					}
					else
					{
						Math3D.VectorScale(right, c, dir);
						Math3D.VectorMA(dir, s, up, dir);
					}
					
					p.alpha = 0.5f;
					//		p.alphavel = -1.0 / (1+frand()*0.2);
					p.alphavel = - 1000.0f;
					//		p.color = 0x74 + (rand()&7);
					p.color = 223 - (Lib.rand() & 7);
					for (j = 0; j < 3; j++)
					{
						p.org[j] = move[j] + dir[j] * 3;
						//			p.vel[j] = dir[j]*6;
						p.vel[j] = 0;
					}
				}
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		// stack variable
		//UPGRADE_NOTE: Final was removed from the declaration of 'r'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] r = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'u'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] u = new float[]{0, 0, 0};
		/*
		* =============== CL_ParticleSteamEffect
		* 
		* Puffs with velocity along direction, with some randomness thrown in
		* ===============
		*/
		internal static void  ParticleSteamEffect(float[] org, float[] dir, int color, int count, int magnitude)
		{
			int i, j;
			cparticle_t p;
			float d;
			
			//		vectoangles2 (dir, angle_dir);
			//		AngleVectors (angle_dir, f, r, u);
			
			Math3D.MakeNormalVectors(dir, r, u);
			
			for (i = 0; i < count; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = color + (Lib.rand() & 7);
				
				for (j = 0; j < 3; j++)
				{
					p.org[j] = org[j] + magnitude * 0.1f * Lib.crand();
					//				p.vel[j] = dir[j]*magnitude;
				}
				Math3D.VectorScale(dir, magnitude, p.vel);
				d = Lib.crand() * magnitude / 3;
				Math3D.VectorMA(p.vel, d, r, p.vel);
				d = Lib.crand() * magnitude / 3;
				Math3D.VectorMA(p.vel, d, u, p.vel);
				
				p.accel[0] = p.accel[1] = 0;
				p.accel[2] = (- CL_fx.PARTICLE_GRAVITY) / 2;
				p.alpha = 1.0f;
				
				p.alphavel = (- 1.0f) / (0.5f + (float) Globals.rnd.NextDouble() * 0.3f);
			}
		}
		
		// stack variable
		// r, u, dir
		internal static void  ParticleSteamEffect2(cl_sustain_t self)
		//	  float[] org, float[] dir, int color, int count, int magnitude)
		{
			int i, j;
			cparticle_t p;
			float d;
			
			//		vectoangles2 (dir, angle_dir);
			//		AngleVectors (angle_dir, f, r, u);
			
			Math3D.VectorCopy(self.dir, dir);
			Math3D.MakeNormalVectors(dir, r, u);
			
			for (i = 0; i < self.count; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = self.color + (Lib.rand() & 7);
				
				for (j = 0; j < 3; j++)
				{
					p.org[j] = self.org[j] + self.magnitude * 0.1f * Lib.crand();
					//				p.vel[j] = dir[j]*magnitude;
				}
				Math3D.VectorScale(dir, self.magnitude, p.vel);
				d = Lib.crand() * self.magnitude / 3;
				Math3D.VectorMA(p.vel, d, r, p.vel);
				d = Lib.crand() * self.magnitude / 3;
				Math3D.VectorMA(p.vel, d, u, p.vel);
				
				p.accel[0] = p.accel[1] = 0;
				p.accel[2] = (- CL_fx.PARTICLE_GRAVITY) / 2;
				p.alpha = 1.0f;
				
				p.alphavel = (- 1.0f) / (0.5f + (float) Globals.rnd.NextDouble() * 0.3f);
			}
			self.nextthink += self.thinkinterval;
		}
		
		// stack variable
		// move, vec, right, up 
		//UPGRADE_NOTE: Final was removed from the declaration of 'forward'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] forward = new float[]{0, 0, 0};
		//UPGRADE_NOTE: Final was removed from the declaration of 'angle_dir'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] angle_dir = new float[]{0, 0, 0};
		/*
		* =============== CL_TrackerTrail ===============
		*/
		internal static void  TrackerTrail(float[] start, float[] end, int particleColor)
		{
			float len;
			cparticle_t p;
			int dec;
			float dist;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			Math3D.VectorCopy(vec, forward);
			Math3D.vectoangles(forward, angle_dir);
			Math3D.AngleVectors(angle_dir, forward, right, up);
			
			dec = 3;
			Math3D.VectorScale(vec, 3, vec);
			
			// FIXME: this is a really silly way to have a loop
			while (len > 0)
			{
				len -= dec;
				
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = - 2.0f;
				p.color = particleColor;
				dist = Math3D.DotProduct(move, forward);
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				Math3D.VectorMA(move, (float) (8 * System.Math.Cos(dist)), up, p.org);
				for (int j = 0; j < 3; j++)
				{
					p.vel[j] = 0;
					p.accel[j] = 0;
				}
				p.vel[2] = 5;
				
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		// stack variable
		// dir
		internal static void  Tracker_Shell(float[] origin)
		{
			cparticle_t p;
			
			for (int i = 0; i < 300; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = CL_fx.INSTANT_PARTICLE;
				p.color = 0;
				
				dir[0] = Lib.crand();
				dir[1] = Lib.crand();
				dir[2] = Lib.crand();
				Math3D.VectorNormalize(dir);
				
				Math3D.VectorMA(origin, 40, dir, p.org);
			}
		}
		
		// stack variable
		// dir
		internal static void  MonsterPlasma_Shell(float[] origin)
		{
			cparticle_t p;
			
			for (int i = 0; i < 40; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = CL_fx.INSTANT_PARTICLE;
				p.color = 0xe0;
				
				dir[0] = Lib.crand();
				dir[1] = Lib.crand();
				dir[2] = Lib.crand();
				Math3D.VectorNormalize(dir);
				
				Math3D.VectorMA(origin, 10, dir, p.org);
				//			VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
				// dir, p.org);
			}
		}
		
		private static int[] wb_colortable = new int[]{2 * 8, 13 * 8, 21 * 8, 18 * 8};
		
		// stack variable
		// dir
		internal static void  Widowbeamout(cl_sustain_t self)
		{
			int i;
			cparticle_t p;
			
			float ratio;
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			ratio = 1.0f - (((float) self.endtime - (float) Globals.cl.time) / 2100.0f);
			
			for (i = 0; i < 300; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = CL_fx.INSTANT_PARTICLE;
				p.color = wb_colortable[Lib.rand() & 3];
				
				dir[0] = Lib.crand();
				dir[1] = Lib.crand();
				dir[2] = Lib.crand();
				Math3D.VectorNormalize(dir);
				
				Math3D.VectorMA(self.org, (45.0f * ratio), dir, p.org);
				//			VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
				// dir, p.org);
			}
		}
		
		private static int[] nb_colortable = new int[]{110, 112, 114, 116};
		
		// stack variable
		// dir
		internal static void  Nukeblast(cl_sustain_t self)
		{
			int i;
			cparticle_t p;
			
			float ratio;
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			ratio = 1.0f - (((float) self.endtime - (float) Globals.cl.time) / 1000.0f);
			
			for (i = 0; i < 700; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = CL_fx.INSTANT_PARTICLE;
				p.color = nb_colortable[Lib.rand() & 3];
				
				dir[0] = Lib.crand();
				dir[1] = Lib.crand();
				dir[2] = Lib.crand();
				Math3D.VectorNormalize(dir);
				
				Math3D.VectorMA(self.org, (200.0f * ratio), dir, p.org);
				//			VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
				// dir, p.org);
			}
		}
		
		private static int[] ws_colortable = new int[]{2 * 8, 13 * 8, 21 * 8, 18 * 8};
		
		// stack variable
		// dir
		internal static void  WidowSplash(float[] org)
		{
			int i;
			cparticle_t p;
			
			for (i = 0; i < 256; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = ws_colortable[Lib.rand() & 3];
				
				dir[0] = Lib.crand();
				dir[1] = Lib.crand();
				dir[2] = Lib.crand();
				Math3D.VectorNormalize(dir);
				Math3D.VectorMA(org, 45.0f, dir, p.org);
				Math3D.VectorMA(Globals.vec3_origin, 40.0f, dir, p.vel);
				
				p.accel[0] = p.accel[1] = 0;
				p.alpha = 1.0f;
				
				p.alphavel = (- 0.8f) / (0.5f + (float) Globals.rnd.NextDouble() * 0.3f);
			}
		}
		
		// stack variable
		// move, vec
		/*
		* ===============
		*  CL_TagTrail
		* ===============
		*/
		internal static void  TagTrail(float[] start, float[] end, float color)
		{
			float len;
			int j;
			cparticle_t p;
			int dec;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			dec = 5;
			Math3D.VectorScale(vec, 5, vec);
			
			while (len >= 0)
			{
				len -= dec;
				
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = (- 1.0f) / (0.8f + (float) Globals.rnd.NextDouble() * 0.2f);
				p.color = color;
				for (j = 0; j < 3; j++)
				{
					p.org[j] = move[j] + Lib.crand() * 16;
					p.vel[j] = Lib.crand() * 5;
					p.accel[j] = 0;
				}
				
				Math3D.VectorAdd(move, vec, move);
			}
		}
		
		/*
		* =============== CL_ColorExplosionParticles ===============
		*/
		internal static void  ColorExplosionParticles(float[] org, int color, int run)
		{
			int i, j;
			cparticle_t p;
			
			for (i = 0; i < 128; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = color + (Lib.rand() % run);
				
				for (j = 0; j < 3; j++)
				{
					p.org[j] = org[j] + ((Lib.rand() % 32) - 16);
					p.vel[j] = (Lib.rand() % 256) - 128;
				}
				
				p.accel[0] = p.accel[1] = 0;
				p.accel[2] = - CL_fx.PARTICLE_GRAVITY;
				p.alpha = 1.0f;
				
				p.alphavel = (- 0.4f) / (0.6f + (float) Globals.rnd.NextDouble() * 0.2f);
			}
		}
		
		// stack variable
		// r, u
		/*
		* =============== CL_ParticleSmokeEffect - like the steam effect, but
		* unaffected by gravity ===============
		*/
		internal static void  ParticleSmokeEffect(float[] org, float[] dir, int color, int count, int magnitude)
		{
			int i, j;
			cparticle_t p;
			float d;
			
			Math3D.MakeNormalVectors(dir, r, u);
			
			for (i = 0; i < count; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = color + (Lib.rand() & 7);
				
				for (j = 0; j < 3; j++)
				{
					p.org[j] = org[j] + magnitude * 0.1f * Lib.crand();
					//				p.vel[j] = dir[j]*magnitude;
				}
				Math3D.VectorScale(dir, magnitude, p.vel);
				d = Lib.crand() * magnitude / 3;
				Math3D.VectorMA(p.vel, d, r, p.vel);
				d = Lib.crand() * magnitude / 3;
				Math3D.VectorMA(p.vel, d, u, p.vel);
				
				p.accel[0] = p.accel[1] = p.accel[2] = 0;
				p.alpha = 1.0f;
				
				p.alphavel = (- 1.0f) / (0.5f + (float) Globals.rnd.NextDouble() * 0.3f);
			}
		}
		
		/*
		* =============== CL_BlasterParticles2
		* 
		* Wall impact puffs (Green) ===============
		*/
		internal static void  BlasterParticles2(float[] org, float[] dir, long color)
		{
			int i, j;
			cparticle_t p;
			float d;
			int count;
			
			count = 40;
			for (i = 0; i < count; i++)
			{
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				
				p.time = Globals.cl.time;
				p.color = color + (Lib.rand() & 7);
				
				d = Lib.rand() & 15;
				for (j = 0; j < 3; j++)
				{
					p.org[j] = org[j] + ((Lib.rand() & 7) - 4) + d * dir[j];
					p.vel[j] = dir[j] * 30 + Lib.crand() * 40;
				}
				
				p.accel[0] = p.accel[1] = 0;
				p.accel[2] = - CL_fx.PARTICLE_GRAVITY;
				p.alpha = 1.0f;
				
				p.alphavel = (- 1.0f) / (0.5f + (float) Globals.rnd.NextDouble() * 0.3f);
			}
		}
		
		// stack variable
		// move, vec
		/*
		* =============== CL_BlasterTrail2
		* 
		* Green! ===============
		*/
		internal static void  BlasterTrail2(float[] start, float[] end)
		{
			float len;
			int j;
			cparticle_t p;
			int dec;
			
			Math3D.VectorCopy(start, move);
			Math3D.VectorSubtract(end, start, vec);
			len = Math3D.VectorNormalize(vec);
			
			dec = 5;
			Math3D.VectorScale(vec, 5, vec);
			
			// FIXME: this is a really silly way to have a loop
			while (len > 0)
			{
				len -= dec;
				
				if (CL_fx.free_particles == null)
					return ;
				p = CL_fx.free_particles;
				CL_fx.free_particles = p.next;
				p.next = CL_fx.active_particles;
				CL_fx.active_particles = p;
				Math3D.VectorClear(p.accel);
				
				p.time = Globals.cl.time;
				
				p.alpha = 1.0f;
				p.alphavel = (- 1.0f) / (0.3f + (float) Globals.rnd.NextDouble() * 0.2f);
				p.color = 0xd0;
				for (j = 0; j < 3; j++)
				{
					p.org[j] = move[j] + Lib.crand();
					p.vel[j] = Lib.crand() * 5;
					p.accel[j] = 0;
				}
				
				Math3D.VectorAdd(move, vec, move);
			}
		}
	}
}